float4x4 World;
float4x4 View;
float4x4 Projection;

float4 AmbientColor;
float AmbientIntensity;

float4 DiffuseColor;
float DiffuseIntensity;
float3 DiffuseLightDirection;

// Specular Variables
float4 SpecularColor;
float SpecularIntensity;
float Shinniness;				// Sharpness the specular highlights. Higher number = sharper highlight
float3 CameraPosition;

// Cel Shader variables
float3 CameraDirection;
int ShadingLevels;          // How many different levels of shading there are. The smaller the number, the less shaded areas
float BorderThickness;      // How thick the border on the objects are
float4 BorderColor;         // Color of the border around the object


struct VertexShaderInput
{
    float4 Position : POSITION0;
	float3 Normal : NORMAL0;
};

struct VertexShaderOutput
{
    float4 Position : POSITION0;
    float3 Normal : TEXCOORD0;
    float3 CameraView : TEXCOORD1;
}; 

VertexShaderOutput VertexShaderFunction( VertexShaderInput input )
{
    VertexShaderOutput output;
	
    float4 worldPosition = mul( input.Position, World );
    float4 viewPosition = mul( worldPosition, View );
    output.Position = mul( viewPosition, Projection );			
    	
	output.Normal = mul( input.Normal, World );
  
	// Get the vector from the camera to the vertex for the specular component by
	// subtracting the world position from the camera
	output.CameraView = normalize( CameraPosition - worldPosition );     
	
    return output;
}

float4 PixelShaderFunction( VertexShaderOutput input ) : COLOR0
{
    // Normalize our variables
    float3 lightdir = normalize( DiffuseLightDirection );
    float3 norm = normalize( input.Normal );
    
    // Calculate the dot product between the normal and the vector from the camera to the object.
    // If it is less than the border thickness, it is part of the border
    if( dot( norm, normalize( CameraPosition ) ) < BorderThickness )
    {
        return BorderColor;
    }
    else
    {
        // Calculate the specular component
        float3 halfAngle = normalize( lightdir + input.CameraView );
        float4 specular = pow( saturate( dot( norm, halfAngle ) ), Shinniness ) * SpecularColor * SpecularIntensity;
        
        // Calculate the diffuse component
        float4 diffuse = dot( lightdir, input.Normal ) * DiffuseColor * DiffuseIntensity;
        // Calculate the ambient component
        float4 ambient = AmbientColor * AmbientIntensity;
        
        // Calculate the final pixel color
        float4 pixelColor = ambient + specular + diffuse;
        // Multiply by the number of shading levels so the color value can range from 0 to ShadingLevels
        pixelColor.rgb *= ShadingLevels;
        // Round to the highest integer. This is what flattens out the color
        pixelColor.rgb = ceil(pixelColor.rgb);
        // Redivide by the number of shading levels to normalize the values to between 0 and 1.
        pixelColor.rgb /= (float)ShadingLevels;
        
        return pixelColor;
    }
}

technique Specular
{
    pass Pass0
    {
        VertexShader = compile vs_1_1 VertexShaderFunction();
        PixelShader = compile ps_2_0 PixelShaderFunction();
    }
}
